package com.chao.yyks.util.webSocket;

import com.chao.common.util.ListUtile;
import com.chao.common.util.StrUtile;
import com.google.gson.Gson;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import okio.ByteString;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 语音合成流式 WebAPI 接口调用示例 接口文档（必看）：https://www.xfyun.cn/doc/tts/online_tts/API.html
 * 发音人使用方式：登陆开放平台https://www.xfyun.cn/后，到控制台-我的应用-语音合成-添加试用或购买发音人，添加后即显示该发音人参数值
 * 错误码链接：https://www.xfyun.cn/document/error-code （code返回错误码时必看）
 * 小语种需要传输小语种文本、使用小语种发音人vcn、tte=unicode以及修改文本编码方式
 *
 * @author xlliu24
 */
@Slf4j
public class WebTtsWs2 {
    // 地址与鉴权信息
    public static final String hostUrl = "https://tts-api.xfyun.cn/v2/tts"; // 普通合成
    // 均到控制台-语音合成页面获取
    public static final String appid = "aeaa1b13";
    public static final String apiSecret = "N2FkODY5ZTYyMjMxN2E0YTg1YjRhMjQ2";
    public static final String apiKey = "53fd1421aa84b0ae83439ea98bd6de73";
    // json
    public static final Gson gson = new Gson();

    public static void main(String[] args) {
        String TEXT_1 = "The Beatles, despite disbanding in 1970, continue to be a hot topic in the film industry, with a surge of related movies and documentaries hitting both cinemas and streaming platforms. Recent releases include the digitally restored 'Let It Be' on Disney+, while 'Midas Man', a biopic about their manager Brian Epstein, is scheduled for October. 'One Hand Clapping', featuring rare footage of Paul McCartney recording with Wings in the early 70s, will soon premiere globally. Additionally, an upcoming documentary titled 'Man on the Run' directed by Morgan Neville focuses on McCartney's life after the Beatles. Looking ahead, director Sam Mendes plans a tetralogy of films, one for each of the Beatles members, set to debut in 2027. Music writer Peter Paphides suggests that the Beatles' story is compelling due to its rich narrative, which includes themes of friendship, love, and exceptional music. He believes the resurgence of interest in the band began with the release of the Anthology TV series and album in the mid-1990s, which attracted both older fans and younger listeners interested in British guitar music. This trend has been sustained by a series of films and documentaries, including 'Nowhere Boy' (2009), 'Living in the Material World' (2011), and 'Yesterday' (2019). Apple Corps, the company responsible for Beatles-related business, has adopted a strategy of continuously releasing new material to maintain the band's prominence. An example is Peter Jackson's 'Get Back', an eight-hour documentary series based on footage from the 'Let It Be' sessions, which became a major television event. This project demonstrates how secondary figures associated with the Beatles, like Brian Epstein, can also attract attention. 'Midas Man', focusing on Epstein's rise from managing a record store to becoming a prominent figure in the entertainment industry, exemplifies this.";


        //x4_enus_gavin_assist 男
        //x4_enus_lucy_education 女
        websocketWork38(Arrays.asList(TEXT_1),"教育女声");
    }

    public static void websocketWork35(List<String> msg,String vcn) {
        if(ListUtile.isEmpty(msg)){
            return;
        }
        for (int i = 0; i < msg.size(); i++) {
            websocketWork(msg.get(i),vcn,35);
            if(i%32 == 0 && i > 0){ // 最多同时生成10个文件, 休眠2分钟等待
                try {
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void websocketWork37(List<String> msg,String vcn) {
        if(ListUtile.isEmpty(msg)){
            return;
        }
        for (int i = 0; i < msg.size(); i++) {
            websocketWork(msg.get(i),vcn,37);
            if(i%32 == 0 && i > 0){ // 最多同时生成10个文件, 休眠2分钟等待
                try {
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void websocketWork38(List<String> msg,String vcn) {
        if(ListUtile.isEmpty(msg)){
            return;
        }
        for (int i = 0; i < msg.size(); i++) {
            websocketWork(msg.get(i),vcn,38);
            if(i%32 == 0 && i > 0){ // 最多同时生成10个文件, 休眠2分钟等待
                try {
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void websocketWork39(List<String> msg,String vcn) {
        if(ListUtile.isEmpty(msg)){
            return;
        }
        for (int i = 0; i < msg.size(); i++) {
            websocketWork(msg.get(i),vcn,39);
            if(i%32 == 0 && i > 0){ // 最多同时生成10个文件, 休眠2分钟等待
                try {
                    Thread.sleep(60000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void websocketWork(String msg,String vcn,int s) {
        try {
            String wsUrl = getAuthUrl(hostUrl, apiKey, apiSecret).replace("https://", "wss://");
            OutputStream outputStream = new FileOutputStream(vcn+StrUtile.getMD5(msg)+".mp3");
            websocketWork(wsUrl,outputStream,msg,vcn,s);
        }catch (Exception e){
            log.error("科大讯飞语音生成失败");
        }
    }

    //文档地址 https://www.xfyun.cn/doc/tts/online_tts/API.html#%E6%8E%A5%E5%8F%A3%E8%B0%83%E7%94%A8%E6%B5%81%E7%A8%8B
    // Websocket方法
    public static void websocketWork(String wsUrl, OutputStream outputStream,String msg,String vcns,int s) {
//        List<Audio> list = new ArrayList<>(); lucy gavin Lindsay
        String vcn = "教育女声".equals(vcns)?"x4_enus_lucy_education":("客服男声".equals(vcns)?"x4_enus_gavin_assist":"x4_EnUs_Lindsay_assist");
        try {
            OkHttpClient client = new OkHttpClient.Builder()
                    .readTimeout(30, TimeUnit.SECONDS)//设置读取超时时间
                    .writeTimeout(30, TimeUnit.SECONDS)//设置写的超时时间
                    .connectTimeout(30, TimeUnit.SECONDS)//设置连接超时时间
                    .build();
            Request request = new Request.Builder().url(wsUrl).build();
            client.newWebSocket(request, new WebSocketListener() {
                @Override
                public void onOpen(WebSocket webSocket, Response response) {
                    log.info("ws建立连接成功...");
                    String requestJson = "{\"common\":{\"app_id\":\""+appid+"\"},\"business\":{\"aue\":\"lame\",\"sfl\":1,\"vcn\":\""+vcn+"\",\"pitch\":50,\"speed\":"+s+"},\"data\":{\"status\":2,\"text\":\""+ Base64.getEncoder().encodeToString(msg.getBytes(StandardCharsets.UTF_8)) +"\"}}";
                    webSocket.send(requestJson);
                }

                @Override
                public void onMessage(WebSocket webSocket, String text) {
//                    System.out.println("返回结果打印：" + text);
                    // System.out.println(text);
                    JsonParse1 myJsonParse = gson.fromJson(text, JsonParse1.class);
                    if (myJsonParse.code != 0) {
                        log.error("发生错误，错误码为：" + myJsonParse.code);
                        log.error("本次请求的sid为：" + myJsonParse.sid);
                    }
                    if (myJsonParse.data != null) {
                        if(!StrUtile.isEmpty(myJsonParse.data.audio)){
                            try {
                                byte[] textBase64Decode = Base64.getDecoder().decode(myJsonParse.data.audio);
                                outputStream.write(textBase64Decode);
                                outputStream.flush();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        if (myJsonParse.data.status == 2) {
                            try {
                                outputStream.close();
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                            log.info("本次请求的sid==>" + myJsonParse.sid);
                            // 可以关闭连接，释放资源
                            webSocket.close(1001,"文件生成结束!");
                        }
                    }
                }

                @Override
                public void onMessage(WebSocket webSocket, ByteString bytes) {
                    super.onMessage(webSocket, bytes);
                }

                @Override
                public void onClosing(WebSocket webSocket, int code, String reason) {
                    super.onClosing(webSocket, code, reason);
                    log.info("socket closing"+reason);
                }

                @Override
                public void onClosed(WebSocket webSocket, int code, String reason) {
                    super.onClosed(webSocket, code, reason);
                    log.info("ws链接已关闭，本次请求完成..."+reason);
                }

                @Override
                public void onFailure(WebSocket webSocket, Throwable t, Response response) {
                    super.onFailure(webSocket, t, response);
                    if (t.getMessage() != null) {
                        log.info("发生错误 " + t.getMessage());
                        webSocket.close(1001,"文件生成失败!");
                    }
                }
            });
        } catch (Exception e) {
            System.out.println(e.getMessage());
        }
    }

    // 鉴权方法
    public static String getAuthUrl(String hostUrl, String apiKey, String apiSecret) throws Exception {
        URL url = new URL(hostUrl);
        // 时间
        SimpleDateFormat format = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss z", Locale.US);
        format.setTimeZone(TimeZone.getTimeZone("GMT"));
        String date = format.format(new Date());
        // 拼接
        String preStr = "host: " + url.getHost() + "\n" + "date: " + date + "\n" + "GET " + url.getPath() + " HTTP/1.1";
        //System.out.println(preStr);
        // SHA256加密
        Mac mac = Mac.getInstance("hmacsha256");
        SecretKeySpec spec = new SecretKeySpec(apiSecret.getBytes(StandardCharsets.UTF_8), "hmacsha256");
        mac.init(spec);
        byte[] hexDigits = mac.doFinal(preStr.getBytes(StandardCharsets.UTF_8));
        // Base64加密
        String sha = Base64.getEncoder().encodeToString(hexDigits);
        // 拼接
        String authorization = String.format("api_key=\"%s\", algorithm=\"%s\", headers=\"%s\", signature=\"%s\"", apiKey, "hmac-sha256", "host date request-line", sha);
        // 拼接地址
        HttpUrl httpUrl = Objects.requireNonNull(HttpUrl.parse("https://" + url.getHost() + url.getPath())).newBuilder().//
                addQueryParameter("authorization", Base64.getEncoder().encodeToString(authorization.getBytes(StandardCharsets.UTF_8))).//
                addQueryParameter("date", date).//
                addQueryParameter("host", url.getHost()).//
                build();

        return httpUrl.toString();
    }
}
